package net.xmmpp.uc.im.gateway.filter;

import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.nio.charset.StandardCharsets;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

@Component
public class IMWebGatewayFilterFactory extends AbstractGatewayFilterFactory<IMWebGatewayFilterFactory.IMWebRouteConfig> {
    private static final Logger LOGGER = LoggerFactory.getLogger(IMWebGatewayFilterFactory.class);

    private final static  String PATH_USERS = "/users/";
    private final static  String PATH_VARIABLE_END_WITH = "/";
    private final static HashFunction hashFunction = Hashing.murmur3_128();

    public IMWebGatewayFilterFactory() {
        super(IMWebRouteConfig.class);
    }

    @Override
    public GatewayFilter apply(IMWebRouteConfig config) {

        return ((exchange, chain) -> {
            ServerHttpRequest request =  exchange.getRequest();
            String rawPath = request.getURI().getRawPath();
            LOGGER.info("rawPath [{}]", rawPath);

            HttpHeaders headers = request.getHeaders();
            HttpMethod httpMethod = request.getMethod();
            MultiValueMap<String, String> queryParams = request.getQueryParams();

            int beginIndex = rawPath.indexOf(PATH_USERS) + PATH_USERS.length();
            int endIndex = rawPath.indexOf(PATH_VARIABLE_END_WITH, beginIndex);
            String username = rawPath.substring(beginIndex, endIndex);

            HashCode hashCode = hashFunction.hashString(username, StandardCharsets.UTF_8);
            int index = Math.abs(hashCode.hashCode()) % 2;
            URI redirectUrl = null;
            if(index == 0){
                redirectUrl = UriComponentsBuilder
                        .fromHttpUrl(config.getBroker0() + rawPath)
                        .queryParams(queryParams)
                        .build()
                        .toUri();
            } else {
                redirectUrl = UriComponentsBuilder
                        .fromHttpUrl(config.getBroker1() + rawPath)
                        .queryParams(queryParams)
                        .build()
                        .toUri();
            }
            LOGGER.info("redirectUrl ---> " + username + ", " + redirectUrl);

            // 生成新的Request对象，该对象放弃了常规路由配置中的spring.cloud.gateway.routes.uri字段
            ServerHttpRequest serverHttpRequest = request.mutate().uri(redirectUrl).method(httpMethod).headers(httpHeaders -> httpHeaders = headers).build();
            // 取出当前的route对象
            Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
            //从新设置Route地址
            Route redirectRoute = Route.async().asyncPredicate(route.getPredicate())
                    .filters(route.getFilters())
                    .id(route.getId())
                    .order(route.getOrder())
                    .uri(redirectUrl)
                    .build();
            // 放回exchange中
            exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, redirectRoute);

            // 链式处理，交给下一个过滤器
            return chain.filter(exchange.mutate().request(serverHttpRequest).build());
        });
    }

    public static class IMWebRouteConfig {
        private String broker0;
        private String broker1;

        public String getBroker0() {
            return broker0;
        }

        public void setBroker0(String broker0) {
            this.broker0 = broker0;
        }

        public String getBroker1() {
            return broker1;
        }

        public void setBroker1(String broker1) {
            this.broker1 = broker1;
        }

        @Override
        public String toString() {
            return "IMWebRouteConfig{" +
                    "broker0='" + broker0 + '\'' +
                    ", broker1='" + broker1 + '\'' +
                    '}';
        }
    }
}
